<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>设计模式——组合模式</title>
    <script type="application/javascript" charset="UTF-8" src="../../common/CommonUtils.js"></script>
    <script type="application/javascript" charset="UTF-8">
        /**
         * 解决 design_composite_01.html 的问题，就需要组合模式
         * 组合模式应用的场景和特点
         *
         * 场景一： 存在一批需要组织成某种层次的关系的对象
         * 场景二： 希望对这批对象中的某一个或某一部分对象实施某一个操作
         *
         * 特点：
         * 在组合模式中只有两种对象：一种是组合对象；一种是叶子对象
         * 这两种的对象都应该实现同一批接口
         * 一般会在组合对象中调用对象方法并隐式的调用下级对象的方法 eg: org.hardWorking('develop);
         */

        /**
         * 场景模拟：
         *  -> 公司
         *  	 -> 北京分公司
         *					-> 财务部门
         *								-> 张1
         *								-> 张2
         *								-> 张3
         *					-> 销售部门
         *								-> 张4
         *								-> 张5
         *								-> 张6
         *      -> 长沙分公司
         *					-> 财务部门
         *								-> 张7
         *								-> 张8
         *								-> 张9
         *					-> 销售部门
         *								-> 张10
         *								-> 张11
         *								-> 张12
         *
         *		实际的任务具体是落实到人上去实施的 也就是说只有人才具有具体的方法实现
         */


        // 创建两种对象需要实现的接口
        var CompositeInterface = new WL.Interface(CompositeInterface, ['addChild','getChild']);
        var LeafInterface = new WL.Interface(LeafInterface, ['hardWorking', 'sleeping']);

        // 创建两种类型的对象
        /**
         * 组合对象
         * @param name
         * @constructor
         */
        var Composite = function (name) {
            this.name = name;
            this.type = 'Composite';
            this.children = [];
            // 在实例化对象的时候，先检测是否已实现接口的全部方法
            WL.Interface.ensureImplements(this, CompositeInterface, LeafInterface);
        };

        // 实现接口
        Composite.prototype = {
            constructor : Composite,
            addChild : function (child) {
                this.children.push(child);
                return this;
            },
            getChild : function (name) {
                // 接收叶子类型对象的数组
                var elements = [];

                // 判断是否为 Leaf 类型对象，如果是，直接加入到 elements 数组中，不是则继续递归调用
                var pushLeaf = function (item) {
                    if (item.type === 'Composite') {
                        item.children.each(arguments.callee);
                    } else if (item.type === 'Leaf') {
                        elements.push(item);
                    }
                };

                // 让 name 指定下的叶子对象执行相关操作
                if (name && this.name !== name) {
                    // 遍历组合对象的 children 进行处理
                    this.children.each(function (item) {

                        // 如果传入的是2级节点 eg:北京分公司
                        if (item.name === name && item.type === 'Composite') {
                            item.children.each(pushLeaf);
                        }
                        // 如果传入的是3级、4级、5级、...N级节点
                        if (item.name !== name && item.type === 'Composite') {
                            item.children.each(arguments.callee);
                        }
                        // 如果传入的是叶子节点
                        if (item.name === name && item.type === 'Leaf') {
                           elements.push(item);
                        }
                    });
                } else {
                    // 让整个公司所有的叶子对象执行相关操作
                    this.children.each(pushLeaf);
                }

                return elements;
            },
            hardWorking : function (name) {
                // 得到 name 下所有的 Leaf 类型的对象
                var leafObjects = this.getChild(name);
                for (var i = 0; i < leafObjects.length; i++) {
                    leafObjects[i].hardWorking();
                }
            },
            sleeping : function (name) {
                // 得到 name 下所有的 Leaf 类型的对象
                var leafObjects = this.getChild(name);
                for (var i = 0; i < leafObjects.length; i++) {
                    leafObjects[i].sleeping();
                }
            }
        };

        /**
         * 叶子对象
         * @param name
         * @constructor
         */
        var Leaf = function (name) {
            this.name = name;
            this.type = 'Leaf';
            // 在实例化对象的时候，先检测是否已实现接口的全部方法
            WL.Interface.ensureImplements(this, CompositeInterface, LeafInterface);
        };

        // 实现接口
        Leaf.prototype = {
            constructor : Leaf,
            addChild : function (child) {
                throw new Error('this method is disabled ...');
            },
            getChild : function (name) {
                if (name) {
                    return this;
                }
                return null;
            },
            hardWorking : function (name) {
                if (name) {
                    document.write(name + ' 努力工作');
                } else {
                    document.write(this.name + ' 努力工作'+'<br>');
                }
            },
            sleeping : function (name) {
                if (name) {
                    document.write(name + ' 好好休息');
                } else {
                    document.write(this.name + ' 好好休息'+'<br>');
                }
            }
        };

        // 测试数据
        var p1 = new Leaf('张1');
        var p2 = new Leaf('张2');
        var p3 = new Leaf('张3');
        var p4 = new Leaf('张4');
        var p5 = new Leaf('张5');
        var p6 = new Leaf('张6');
        var p7 = new Leaf('张7');
        var p8 = new Leaf('张8');
        var p9 = new Leaf('张9');
        var p10 = new Leaf('张10');
        var p11 = new Leaf('张11');
        var p12 = new Leaf('张12');
        var dept1 = new Composite('北京开发部门');
        dept1.addChild(p1).addChild(p2).addChild(p3);
        var dept2 = new Composite('北京销售部门');
        dept2.addChild(p4).addChild(p5).addChild(p6);
        var dept3 = new Composite('长沙开发部门');
        dept3.addChild(p7).addChild(p8).addChild(p9);
        var dept4 = new Composite('长沙销售部门');
        dept4.addChild(p10).addChild(p11).addChild(p12);
        var org1 = new Composite('北京分公司');
        org1.addChild(dept1).addChild(dept2);
        var org2 = new Composite('长沙分公司');
        org2.addChild(dept3).addChild(dept4);

        var org = new Composite('尚学堂总部');
        org.addChild(org1).addChild(org2);

        // 让整个公司下所有的员工都去努力工作
        org.hardWorking();	 //尚学堂总部
        document.write('<hr>');
        // name为总公司的直接子节点的时候
        org.hardWorking('长沙分公司');
        document.write('<hr>');
        // name为总公司的间接子节点的时候（类型不为leaf）(3级4级...N级)
        org.hardWorking('长沙开发部门');
        document.write('<hr>');
        // name为leaf对象的时候
        org.hardWorking('张5');
        document.write('<hr>');

    </script>
</head>
<body>

</body>
</html>